package com.musicsonar.controller;

import it.sauronsoftware.jave.AudioAttributes;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.EncodingAttributes;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.lf5.util.StreamUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import pe.com.trestperu.common.exception.CoreServiceException;
import pe.com.trestperu.common.listener.ContextLoaderListener;
import pe.com.trestperu.common.service.GenericService;
import pe.com.trestperu.common.util.FlvAudioStream;
import pe.com.trestperu.common.util.SendMail;

import com.musicsonar.common.domain.AudioInformation;
import com.musicsonar.common.domain.PlayerDetailList;
import com.musicsonar.common.domain.PlayerList;
import com.musicsonar.common.domain.PlayerLogging;
import com.musicsonar.common.domain.PlayerUser;

@SuppressWarnings("unchecked")
@Controller
public class MusicSonarController {

	static final Log log = LogFactory.getLog(MusicSonarController.class);

	static final String APIkey = "187475498032439";

	static final String APIsecret = "8d65c9ec8ee417abeac7fa34bbd3f0da";

	static final String APP_SERVER_URL = "http://yumiusik.dyndns.org:17070/player/";

	static final String SERVICE_COUNTRY_CONVERT = "http://ip2country.hackers.lv/api/ip2country?ip=";

	static final String MP3_DOWNLOAD_SNIPMP3_SERVICE = "http://www.snipmp3.com/?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D";

	static final String STATIC_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1";

	static final String YOUTUBE_GENERAL_URL = "http://www.youtube.com/watch?v=";

	static final String ERROR_MSG = "$$$ERROR$$$";

	static final String MAIL_USER_FROM = "system@musicsonar.com";

	static final String MAIL_USER_TO = "ttataje@gmail.com.pe";

	public static class TYPE_ACCESS {
		public static final String Search = "SR";
		public static final String Verify = "VR";
		public static final String Play = "PL";
		public static final String GetInformation = "GI"; 
	}

	private GenericService getGenericService(){
		return (GenericService)ContextLoaderListener.getBean(GenericService.class);
	}

	@RequestMapping(value = "/init")
	public String init(HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("init :: inicio");
		log.info("RemoteHost :: " + request.getRemoteHost());
		log.info("User-Agent :: " + request.getHeader("User-Agent"));
		log.info("init :: fin");
		return "init";
	}

	@RequestMapping(value = "/info")
	public @ResponseBody String info(@RequestParam("url") String urlYouTube, @RequestParam("tipo") String tipo, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("info :: inicio");
		// tipo ==>  Parametro que define si se regresa informacion de tipo ( high, mid, low )
		if(!urlYouTube.startsWith("http://")){
			urlYouTube = "http://" + urlYouTube;
		}
		log.info("urlYouTube :: " + urlYouTube);
		log.info("User-Agent :: " + request.getHeader("User-Agent"));
		String video_id = getVideoIdFromURL(urlYouTube);
		AudioInformation audioInformation = (AudioInformation)getGenericService().findById(AudioInformation.class, video_id);

		Map<String, Object> information = new HashMap<String, Object>();

		if(audioInformation == null){
			URL url = new URL("http://gdata.youtube.com/feeds/api/videos/" + video_id);
			HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
			cnx.setDoInput(true);
			cnx.setDoOutput(true);
			cnx.setRequestMethod("GET");
	
			// Leyendo la respuesta
			InputStream in = cnx.getInputStream();
			Document doc = readXml(in);
			NodeList nodeList = doc.getElementsByTagName("media:content");
			Node node = nodeList.item(0);
	
			information.put("duration", node.getAttributes().getNamedItem("duration").getTextContent());
			nodeList = doc.getElementsByTagName("media:thumbnail");
			node = nodeList.item(0);
			information.put("image", node.getAttributes().getNamedItem("url").getTextContent());
	
			nodeList = doc.getElementsByTagName("media:title");
			node = nodeList.item(0);
			information.put("title", node.getTextContent());
	
			information.put("mp3", APP_SERVER_URL+ "mp3"+tipo+"/?url="+urlYouTube);
	
			in.close();

			audioInformation = (AudioInformation)ContextLoaderListener.getBean(AudioInformation.class);
			audioInformation.setVideo_id(video_id);
			audioInformation.setDuration(MapUtils.getLong(information, "duration"));
			audioInformation.setImage(MapUtils.getString(information, "image"));
			audioInformation.setTitle(MapUtils.getString(information, "title"));
			audioInformation.setLink(urlYouTube);
			audioInformation.setLength(new Long(-1));
			audioInformation.setValidate("NO");
			getGenericService().save(audioInformation);
		}else{
			information.put("id", audioInformation.getVideo_id());
			information.put("duration", audioInformation.getDuration());
			information.put("image", audioInformation.getImage());
			information.put("title", audioInformation.getTitle());
			information.put("length", audioInformation.getLength());
			information.put("link", audioInformation.getLink());
			information.put("mp3", APP_SERVER_URL+"mp3"+tipo+"/?url="+urlYouTube);
		}

		response.setContentType("text/xml");
		
		PrintWriter out = response.getWriter();
		out.print(toXML(information, "information"));
		out.flush();
		out.close();

		log.info("info :: fin");
		return null;
	}

	@RequestMapping(value = "/verifyMP3")
	public void verifyMP3(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("verifyMP3 :: inicio");
		if(!urlYouTube.startsWith("http://")){
			urlYouTube = "http://" + urlYouTube;
		}
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("estado", "OK");
		result.put("responseText", "OK");
		String video_id = getVideoIdFromURL(urlYouTube);
		saveLog(request, TYPE_ACCESS.Verify, null, video_id);
		try{
			File mp3 = returnMP3File(video_id);
			if(!mp3.exists()){
				result.put("estado", "ERROR");
				result.put("responseText", "Can't access to request file");
			}
		}catch(Exception e){
			result.put("estado", "ERROR");
			result.put("responseText", e.getMessage());
			sendEmail(video_id);
		}
		PrintWriter out = response.getWriter();	
		out.print("[["+(new ObjectMapper()).writeValueAsString(result)+"]]");
		out.flush();
		out.close();
		log.info("verifyMP3 :: fin");
	}

	@RequestMapping(value = "/mp3")
	public void mp3(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		mp3high(urlYouTube, request, response);
	}

	@RequestMapping(value = "/mp3mobile")
	public void mp3mobile(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		mp3low(urlYouTube, request, response);
	}

	@RequestMapping(value = "/mp3low")
	public void mp3low(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("mp3low :: inicio");
		if(!urlYouTube.startsWith("http://")){
			urlYouTube = "http://" + urlYouTube;
		}
		String video_id = getVideoIdFromURL(urlYouTube);
		saveLog(request, TYPE_ACCESS.Play, "low", video_id);
		AudioInformation audioInformation = (AudioInformation)getGenericService().findById(AudioInformation.class, video_id);

		if(audioInformation == null){
			audioInformation = createAudioInformation(video_id);
		}

		File mp3 = returnMP3LowFile(video_id);

		// Leyendo la respuesta
		FileInputStream fileInputStream = new FileInputStream(mp3);

		response.addHeader("Content-Type","application/octet-stream;");
		response.addHeader("Content-Length", String.valueOf(mp3.length()));
		response.addHeader("Accept-Ranges", "bytes");
		response.addHeader("Content-Disposition", "attachment; filename=\"" + audioInformation.getTitle() + ".mp3\"");

		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		
		long length = mp3.length();

		byte[] buffer = new byte[1024];
		int read = fileInputStream.read(buffer);
		while (read >= 0){
			if (read > 0){
				out.write(buffer, 0, read);
			}
			read = fileInputStream.read(buffer);
			length += buffer.length;
		}

		out.flush();
		out.close();
		fileInputStream.close();

		if(audioInformation.getLength().longValue() != length){
			audioInformation.setLength(new Long(length));
			getGenericService().save(audioInformation);
		}
		log.info("mp3low :: fin");
	}

	@RequestMapping(value = "/mp3mid")
	public void mp3mid(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("mp3high :: inicio");
		if(!urlYouTube.startsWith("http://")){
			urlYouTube = "http://" + urlYouTube;
		}
		String video_id = getVideoIdFromURL(urlYouTube);
		saveLog(request, TYPE_ACCESS.Play, "mid", video_id);
		AudioInformation audioInformation = (AudioInformation)getGenericService().findById(AudioInformation.class, video_id);

		if(audioInformation == null){
			audioInformation = createAudioInformation(video_id);
		}

		File mp3 = returnMP3MidFile(video_id);

		// Leyendo la respuesta
		FileInputStream fileInputStream = new FileInputStream(mp3);

		response.addHeader("Content-Type","application/octet-stream;");
		response.addHeader("Content-Length", String.valueOf(mp3.length()));
		response.addHeader("Accept-Ranges", "bytes");
		response.addHeader("Content-Disposition", "attachment; filename=\"" + audioInformation.getTitle() + ".mp3\"");

		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		
		long length = mp3.length();

		byte[] buffer = new byte[1024];
		int read = fileInputStream.read(buffer);
		while (read >= 0){
			if (read > 0){
				out.write(buffer, 0, read);
			}
			read = fileInputStream.read(buffer);
			length += buffer.length;
		}

		out.flush();
		out.close();
		fileInputStream.close();

		if(audioInformation.getLength().longValue() != length){
			audioInformation.setLength(new Long(length));
			getGenericService().save(audioInformation);
		}
		log.info("mp3 :: fin");
	}

	@RequestMapping(value = "/mp3high")
	public void mp3high(@RequestParam("url") String urlYouTube, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("mp3high :: inicio");
		if(!urlYouTube.startsWith("http://")){
			urlYouTube = "http://" + urlYouTube;
		}
		String video_id = getVideoIdFromURL(urlYouTube);
		saveLog(request, TYPE_ACCESS.Play, "high", video_id);
		AudioInformation audioInformation = (AudioInformation)getGenericService().findById(AudioInformation.class, video_id);

		if(audioInformation == null){
			audioInformation = createAudioInformation(video_id);
		}

		File mp3 = returnMP3HighFile(video_id);

		// Leyendo la respuesta
		FileInputStream fileInputStream = new FileInputStream(mp3);

		response.addHeader("Content-Type","application/octet-stream;");
		response.addHeader("Content-Length", String.valueOf(mp3.length()));
		response.addHeader("Accept-Ranges", "bytes");
		response.addHeader("Content-Disposition", "attachment; filename=\"" + audioInformation.getTitle() + ".mp3\"");

		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		
		long length = mp3.length();

		byte[] buffer = new byte[1024];
		int read = fileInputStream.read(buffer);
		while (read >= 0){
			if (read > 0){
				out.write(buffer, 0, read);
			}
			read = fileInputStream.read(buffer);
			length += buffer.length;
		}

		out.flush();
		out.close();
		fileInputStream.close();

		if(audioInformation.getLength().longValue() != length){
			audioInformation.setLength(new Long(length));
			getGenericService().save(audioInformation);
		}
		log.info("mp3high :: fin");
	}

	private AudioInformation createAudioInformation(String video_id) throws IOException, SAXException, ParserConfigurationException, CoreServiceException{
		URL url = new URL("http://gdata.youtube.com/feeds/api/videos/" + video_id);
		HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
		cnx.setDoInput(true);
		cnx.setDoOutput(true);
		cnx.setRequestMethod("GET");

		AudioInformation audioInformation = (AudioInformation)ContextLoaderListener.getBean(AudioInformation.class);
		audioInformation.setVideo_id(video_id);

		// Leyendo la respuesta
		InputStream in = cnx.getInputStream();
		Document doc = readXml(in);
		NodeList nodeList = doc.getElementsByTagName("yt:duration");
		Node node = nodeList.item(0);

		audioInformation.setDuration(new Long(node.getAttributes().getNamedItem("seconds").getTextContent()));
		nodeList = doc.getElementsByTagName("media:thumbnail");
		node = nodeList.item(0);
		audioInformation.setImage(node.getAttributes().getNamedItem("url").getTextContent());

		nodeList = doc.getElementsByTagName("media:title");
		node = nodeList.item(0);
		audioInformation.setTitle(node.getTextContent());

		in.close();
		
		audioInformation.setLink(YOUTUBE_GENERAL_URL+video_id);
		audioInformation.setLength(new Long(-1));
		audioInformation.setValidate("NO");
		getGenericService().save(audioInformation);

		return audioInformation;
	}

	public static String toXML(Map<String, Object> map, String root) {
		StringBuilder sb = new StringBuilder("<");
		sb.append(root);
		sb.append(">");

		for (Map.Entry<String, Object> e : map.entrySet()) {
			sb.append("<");
			sb.append(e.getKey());
			sb.append(">");

			sb.append(e.getValue());

			sb.append("</");
			sb.append(e.getKey());
			sb.append(">");
		}

		sb.append("</");
		sb.append(root);
		sb.append(">");

		return sb.toString();
	}

	public static Document readXml(InputStream is) throws SAXException, IOException, ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

		dbf.setValidating(false);
		dbf.setIgnoringComments(false);
		dbf.setIgnoringElementContentWhitespace(true);
		dbf.setNamespaceAware(true);
		// dbf.setCoalescing(true);
		// dbf.setExpandEntityReferences(true);

		DocumentBuilder db = null;
		db = dbf.newDocumentBuilder();
		db.setEntityResolver(new NullResolver());

		return db.parse(is);
	}

	private static String getVideoIdFromURL(String urlYouTube) throws MalformedURLException{
		URL url = new URL(urlYouTube);
		String[] query = StringUtils.split(url.getQuery(), "&");
		for (int i = 0; i < query.length; i++) {
			if (query[i].startsWith("v")) {
				String[] part = StringUtils.split(query[i], "=");
				return part[1];
			}
		}
		return null;
	}

	@RequestMapping(value="/search")
	@ResponseBody
	public void search(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String urlYouTube = "http://gdata.youtube.com/feeds/api/videos?"+request.getQueryString();
	
		saveLog(request, TYPE_ACCESS.Search, null, null);
		
		URL url = new URL(urlYouTube);	
		HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
		cnx.setDoInput(true);
		cnx.setDoOutput(true);
		cnx.setRequestMethod("GET");

		InputStream in = cnx.getInputStream();
		String sb = new String(StreamUtils.getBytes(in), "UTF-8");
		String content_type = cnx.getContentType();
		in.close();
		cnx.disconnect();
		sb = StringUtils.replace(sb,"href='http://gdata.youtube.com/feeds/api/videos?","href='"+APP_SERVER_URL+"search/?");
		response.setContentType(content_type);
		PrintWriter out = response.getWriter();
		out.print(sb.toString());
		out.flush();
		out.close();
	}

	public File returnMP3File(String video_id) throws IOException{
		File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
		if(!mp3.exists()){
			mp3 = downloadMP3(video_id);
		}
		return mp3;
	}

	public File returnMidMP3File(String video_id) throws IOException{
		File midmp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_mid_youtube"+File.separatorChar+video_id+".mp3");
		if(!midmp3.exists()){
			File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
			if(!mp3.exists()){
				mp3 = returnMP3File(video_id);
			}
			AudioAttributes audio = new AudioAttributes();
			audio.setCodec("libmp3lame");
			audio.setBitRate(new Integer(64000));
			audio.setChannels(new Integer(2));
			audio.setSamplingRate(new Integer(22050));
			EncodingAttributes attrs = new EncodingAttributes();
			attrs.setFormat("mp3");
			attrs.setAudioAttributes(audio);
			Encoder encoder = new Encoder();
			try{
				encoder.encode(mp3, midmp3, attrs);
			}catch(Exception e){
				log.error(e);
				if(midmp3.exists()){
					midmp3.delete();
				}
				sendErrorEmail(e);
			}
		}
		return midmp3;
	}

	public File returnMP3LowFile(String video_id) throws IOException{
		File mp3low = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_low_youtube"+File.separatorChar+video_id+".mp3");
		if(!mp3low.exists()){
			File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
			if(!mp3.exists()){
				mp3 = returnMP3File(video_id);
			}
			AudioAttributes audio = new AudioAttributes();
			audio.setCodec("libmp3lame");
			audio.setBitRate(new Integer(32000));
			audio.setChannels(new Integer(1));
			audio.setSamplingRate(new Integer(22050));
			EncodingAttributes attrs = new EncodingAttributes();
			attrs.setFormat("mp3");
			attrs.setAudioAttributes(audio);
			Encoder encoder = new Encoder();
			try{
				encoder.encode(mp3, mp3low, attrs);
			}catch(Exception e){
				log.error(e);
				if(mp3low.exists()){
					mp3low.delete();
				}
				sendErrorEmail(e);
			}
		}
		return mp3low;
	}
	
	public File returnMP3MidFile(String video_id) throws IOException{
		File mp3mid = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_mid_youtube"+File.separatorChar+video_id+".mp3");
		if(!mp3mid.exists()){
			File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
			if(!mp3.exists()){
				mp3 = returnMP3File(video_id);
			}
			AudioAttributes audio = new AudioAttributes();
			audio.setCodec("libmp3lame");
			audio.setBitRate(new Integer(64000));
			audio.setChannels(new Integer(2));
			audio.setSamplingRate(new Integer(22050));
			EncodingAttributes attrs = new EncodingAttributes();
			attrs.setFormat("mp3");
			attrs.setAudioAttributes(audio);
			Encoder encoder = new Encoder();
			try{
				encoder.encode(mp3, mp3mid, attrs);
			}catch(Exception e){
				log.error(e);
				if(mp3mid.exists()){
					mp3mid.delete();
				}
				sendErrorEmail(e);
			}
		}
		return mp3mid;
	}

	public File returnMP3HighFile(String video_id) throws IOException{
		File mp3high = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_high_youtube"+File.separatorChar+video_id+".mp3");
		if(!mp3high.exists()){
			File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
			if(!mp3.exists()){
				mp3 = returnMP3File(video_id);
			}
			AudioAttributes audio = new AudioAttributes();
			audio.setCodec("libmp3lame");
			audio.setBitRate(new Integer(128000));
			audio.setChannels(new Integer(2));
			audio.setSamplingRate(new Integer(44100));
			EncodingAttributes attrs = new EncodingAttributes();
			attrs.setFormat("mp3");
			attrs.setAudioAttributes(audio);
			Encoder encoder = new Encoder();
			try{
				encoder.encode(mp3, mp3high, attrs);
			}catch(Exception e){
				log.error(e);
				if(mp3high.exists()){
					mp3high.delete();
				}
				sendErrorEmail(e);
			}
		}
		return mp3high;
	}

	@RequestMapping(value = "/login")
	public ResponseEntity<Map<String, String>> login(@RequestParam("user") String username, @RequestParam("facebookID") String facebookID, @RequestParam("pass") String password, @RequestParam("type") String type, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("login :: inicio");
		log.info("User-Agent :: " + request.getHeader("User-Agent"));
		ObjectMapper mapper = new ObjectMapper();
		Map<String, String> result = new HashMap<String, String>();
		result.put("estado", "OK");
		Map<String, String> params = new HashMap<String, String>();
		params.put("email", username);
		params.put("facebookID", facebookID);

		List<Map<String, Object>> userList = (List<Map<String, Object>>)getGenericService().mapListar("youTubePlayer.getPlayerUser", params);
		if(CollectionUtils.isNotEmpty(userList)){
			Map<String, Object> user = userList.get(0);
			if(!password.equals(MapUtils.getString(user, "PASS"))){
				result.put("estado", "ERROR");
				result.put("errorCode", "1");
				result.put("message", "User password error");
				return new ResponseEntity<Map<String, String>>(result, HttpStatus.METHOD_FAILURE);
			}
			user.remove("PASS");
			result.put("user", mapper.writeValueAsString(userList.get(0)));
		}else{
			result.put("estado", "ERROR");
			result.put("errorCode", "0");
			result.put("message", "Can't find user");
			return new ResponseEntity<Map<String, String>>(result, HttpStatus.METHOD_FAILURE);
		}
		log.info("login :: fin");

		return new ResponseEntity<Map<String, String>>(result, HttpStatus.ACCEPTED);
	}

	@RequestMapping(value="/getAccountID")
	@ResponseBody
	public void getAccountID(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String username = request.getParameter("username");
		String facebookID = request.getParameter("facebookID");

		Map<String, String> params = new HashMap<String, String>();
		params.put("email", username);
		params.put("facebookID", null);

		List<Map<String, Object>> userList = (List<Map<String, Object>>)getGenericService().mapListar("youTubePlayer.getPlayerUser", params);

		response.setContentType("text/plain");
		PrintWriter out = response.getWriter();	
		if(CollectionUtils.isNotEmpty(userList)){
			Map<String, Object> item = userList.get(0);
			if(StringUtils.isNotEmpty(facebookID) && StringUtils.isEmpty(MapUtils.getString(item, "FACEBOOKID"))){
				PlayerUser user = (PlayerUser)getGenericService().findById(PlayerUser.class, MapUtils.getLong(item, "CODE"));
				user.setFacebookID(facebookID);
				getGenericService().save(user);
			}
			out.print(MapUtils.getLong(item, "CODE"));
		}else{
			PlayerUser user = new PlayerUser();
			user.setEmail(username);
			user.setUserType("F");
			getGenericService().save(user);
			out.print(String.valueOf(user.getCode()));
		}
		out.flush();
		out.close();
	}

	@RequestMapping(value="/getPlayListShared")
	@ResponseBody
	public void getPlayListShared(HttpServletRequest request, HttpServletResponse response) throws Exception {
		response.setContentType("text/xml");
		PrintWriter out = response.getWriter();	
		out.print("");
		out.flush();
		out.close();
	}

	@RequestMapping(value="/sharePlayList")
	@ResponseBody
	public void sharePlayList(HttpServletRequest request, HttpServletResponse response) throws Exception {
		PrintWriter out = response.getWriter();	
		out.print("");
		out.flush();
		out.close();
	}

	@RequestMapping(value="/savePlayList")
	@ResponseBody
	public void savePlayList(HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("savePlayList :: inicio");
		String xml = request.getParameter("xml");
		Long playerUser = new Long(request.getParameter("userID"));
		String playListName = request.getParameter("name");
		Long playListID = new Long(request.getParameter("code"));
		PlayerList playList = new PlayerList();
		if(playListID.longValue() == -1){
			playList.setActive("SI");
			playList.setListName(playListName);
			playList.setPlayerUser(playerUser);
			getGenericService().save(playList);
		}else{
			playList = (PlayerList)getGenericService().findById(PlayerList.class, playListID);
		}
		Map<String, Long> params = new HashMap<String, Long>();
		params.put("listCode", playListID);
		getGenericService().executeVoidProcedure("youtubePlayer.deletPlayerListDetail", params);
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		try{
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document dom = builder.parse(new ByteArrayInputStream(xml.getBytes()));
			Element root = dom.getDocumentElement();
			NodeList items = root.getChildNodes();
			for (int i = 0; i < items.getLength(); i++){
				HashMap<String, Object> data = new HashMap<String, Object>();
				Node item = items.item(i);
				NodeList information = item.getChildNodes();
				for(int j = 0; j < information.getLength(); j++){
					Node nodo = information.item(j);
					data.put(nodo.getNodeName(), nodo.getTextContent());
				}
				PlayerDetailList detail = new PlayerDetailList();
				detail.setListCode(playList.getCode());
				detail.setPlayOrder(new Long(i));
				detail.setSongID(getVideoIdFromURL(MapUtils.getString(data, "songYT")));
				getGenericService().save(detail);
			}
		}catch(Exception e){
			log.error(e);
		}
		PrintWriter out = response.getWriter();
		out.print(String.valueOf(playList.getCode()));
		out.flush();
		out.close();
		log.info("savePlayList :: fin");
	}

	@RequestMapping(value = "/returnListFromID")
	public void returnListFromID(@RequestParam("playlistID") String playlistID, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("returnListFromID :: inicio");
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("estado", "OK");
		result.put("responseText", "OK");
		try{
			PlayerList playerList = (PlayerList)getGenericService().findById(PlayerList.class, new Long(playlistID));
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("listCode", playerList.getCode());
			List<Map<String, String>> list = getGenericService().mapListar("youtubePlayer.getPlayerListDetail", params);
			result.put("list", list);
		}catch(Exception e){
			result.put("estado", "ERROR");
			result.put("responseText", e.getMessage());
		}
		PrintWriter out = response.getWriter();	
		out.print((new ObjectMapper()).writeValueAsString(result));
		out.flush();
		out.close();
		log.info("returnListFromID :: fin");
	}

	@RequestMapping(value = "/returnSongFromID")
	public void returnSongFromID(@RequestParam("songID") String songID, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("returnSongFromID :: inicio");
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("estado", "OK");
		result.put("responseText", "OK");
		try{
			AudioInformation audioInformation = (AudioInformation)getGenericService().findById(AudioInformation.class, songID);
			Map<String, Object> song = new HashMap<String, Object>();
			song.put("codigo", audioInformation.getVideo_id());
			song.put("link", audioInformation.getLink());
			song.put("title", audioInformation.getTitle());
			song.put("image_link", audioInformation.getImage());
			song.put("segundos", audioInformation.getDuration());
			//audioInformation
			result.put("result", song);
		}catch(Exception e){
			result.put("estado", "ERROR");
			result.put("responseText", e.getMessage());
		}
		PrintWriter out = response.getWriter();	
		out.print((new ObjectMapper()).writeValueAsString(result));
		out.flush();
		out.close();
		log.info("returnSongFromID :: fin");
	}

	@RequestMapping(value = "/getListDetail")
	public void getListDetail(@RequestParam("playlistID") String playlistID, HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.info("getListDetail :: inicio");
		try{
			PlayerList playerList = (PlayerList)getGenericService().findById(PlayerList.class, new Long(playlistID));
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("listCode", playerList.getCode());
			List<Map<String, String>> list = getGenericService().mapListar("youtubePlayer.getPlayerListDetail", params);
			response.setContentType("text/plain");
			PrintWriter out = response.getWriter();	
			out.print(convertToString(list));
			out.flush();
			out.close();
		}catch(Exception e){
			e.printStackTrace();
		}
		log.info("getListDetail :: fin");
	}

	private String convertToString(List<Map<String, String>> songList){
		String result = "<ul>";
		for(Map<String, String> song : songList){
			result += "<li>";
			result += MapUtils.getString(song, "title");
			result += "</li>";
		}
		result += "</ul>";
		return result;
	}

	private void saveLog(HttpServletRequest request, String typeAccess, String quality, String video_id) throws Exception {
		String ip_remote = request.getHeader("x-forwarded-for") != null ? request.getHeader("x-forwarded-for") : request.getRemoteHost();
		PlayerLogging logging = (PlayerLogging)ContextLoaderListener.getBean(PlayerLogging.class);
		logging.setUser_agent(request.getHeader("User-Agent"));
		logging.setIp_address(ip_remote);
		logging.setCountry_code(getCountryCodeFromIP(ip_remote));
		logging.setType_log(typeAccess);
		logging.setQuality(quality);
		logging.setVideo_id(video_id);
		if(TYPE_ACCESS.GetInformation.equals(typeAccess)){
			// Do something special for this kind of access
		}
		if(TYPE_ACCESS.Search.equals(typeAccess)){
			// fill vq and page_number
			Map<String, String> params = convertQStringToMap(request.getQueryString());
			logging.setVq(params.get("vq"));
			logging.setPage_number(params.get("start-index"));
		}
		if(TYPE_ACCESS.Verify.equals(typeAccess)){
			// Do something special for this verification
		}
		if(TYPE_ACCESS.Play.equals(typeAccess)){
			// Do something special for this playing
		}
		if(StringUtils.isNotEmpty(logging.getIp_address()))
			getGenericService().save(logging);
	}

	private String getCountryCodeFromIP(String ip) throws Exception{
		URL url = new URL(SERVICE_COUNTRY_CONVERT + ip);	
		HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
		cnx.setDoInput(true);
		cnx.setDoOutput(true);
		cnx.setRequestMethod("GET");

		InputStream in = cnx.getInputStream();
		StringBuffer sb = new StringBuffer();
		int c;
		while ((c = in.read()) != -1) sb.append((char) c);	
		in.close();

		ObjectMapper mapper = new ObjectMapper();
		Map<?, ?> map = mapper.readValue(sb.toString(), HashMap.class);
		Map<String, Object> _c = MapUtils.getMap(map, "c");
		return MapUtils.getString(_c, ip);
	}

	private Map<String, String> convertQStringToMap(String qstring){
		Map<String, String> result = new HashMap<String, String>();
		String[] first = StringUtils.split(qstring, "&");
		for(String item : first){
			String[] second = StringUtils.split(item, "=");
			result.put(second[0], second[1]);
		}
		return result;
	}

	private File downloadMP3(String video_id) throws IOException{
		File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
		try{
			String parameters = "video_id=" + video_id + "&ac=www&t=grp&r=1381734470826";
			URL url = new URL("http://www.youtube-mp3.org/a/itemInfo/?" + parameters);
			HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
			cnx.setRequestMethod("GET");

			// Leyendo la respuesta
			InputStream in = cnx.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			StringBuilder htmlResult = new StringBuilder();

			String linea = null;
			while ((linea = reader.readLine()) != null) {
				htmlResult.append(linea);
			}
			in.close();
			ObjectMapper mapper = new ObjectMapper();
			Map<String, Object> map = mapper.readValue(StringUtils.substring(htmlResult.toString(), htmlResult.toString().indexOf("{"), htmlResult.toString().length()), HashMap.class);

			// Descargando
			parameters = "video_id=" + video_id + "&h=" + MapUtils.getString(map, "h") + "&r=1330648816742";
			url = new URL("http://www.youtube-mp3.org/get?" + parameters);
			cnx = (HttpURLConnection) url.openConnection();
			cnx.setRequestMethod("GET");

			InputStream inputStream = cnx.getInputStream();
			FileUtils.copyInputStreamToFile(inputStream, mp3);
			inputStream.close();
			cnx.disconnect();
		}catch(Exception ex){
			byte arrayOfByte[] = new byte[512];

			File directorio = mp3.getParentFile();
			if(!directorio.exists()){
				directorio.mkdirs();
			}
			FileOutputStream out = new FileOutputStream(mp3);
			String flv_url = getFLVLink(video_id);
			if(flv_url != null){
				log.info("flv_url :: " + flv_url);
				URL url = new URL(flv_url);
				HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
				InputStream in = null;
				try{
					in = cnx.getInputStream();
				}catch(Exception e){
					if(out != null){
						out.close();
					}
					mp3.delete();
					throw new IOException(e);
				}
				File tmpFile = File.createTempFile(video_id, "mp4", FileUtils.getTempDirectory());
				FileOutputStream fos = new FileOutputStream(tmpFile);
				int i;
				while ((i = in.read(arrayOfByte, 0, 512)) > 0)  {
					fos.write(arrayOfByte, 0, i);
				}
				fos.flush();
				fos.close();

				AudioAttributes audio = new AudioAttributes();
				audio.setCodec("libmp3lame");
				audio.setBitRate(new Integer(128000));
				audio.setChannels(new Integer(2));
				audio.setSamplingRate(new Integer(44100));
				EncodingAttributes attrs = new EncodingAttributes();
				attrs.setFormat("mp3");
				attrs.setAudioAttributes(audio);
				Encoder encoder = new Encoder();
				try{
					encoder.encode(mp3, tmpFile, attrs);
				}catch(Exception e){
					FlvAudioStream audioStream = null;
					try{
						audioStream = new FlvAudioStream(cnx.getInputStream());
					}catch(Exception x){
						if(out != null){
							out.close();
						}
						mp3.delete();
						throw new IOException(x);
					}
					if(audioStream != null){
						int y;
						while ((y = audioStream.read(arrayOfByte, 0, 512)) > 0)  {
							out.write(arrayOfByte, 0, y);
						}

						audioStream.close();
					}
				}

				if(out != null){
					out.close();
				}

				cnx.disconnect();
			}else{
				if(out != null){
					out.close();
				}

				if(mp3.exists()){
					mp3.delete();
				}
				throw new IOException("Can't get Link from Info");
			}
		}

		return mp3;
	}

	private String getFLVLink(String video_id){
		String flv_url = "http://youtube.com/get_video_info?video_id=" + video_id+"&asv=3&el=detailpage&hl=en_US";
		HttpURLConnection cnx = null;
		try{
			URL url = new URL(flv_url);
			cnx = (HttpURLConnection) url.openConnection();
			cnx.setRequestMethod("GET");
			cnx.setRequestProperty("User-Agent", STATIC_USER_AGENT);
			cnx.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			cnx.setUseCaches(false);
			cnx.setDoInput(true);
			cnx.setDoOutput(true);
			String content_encoding = "UTF-8";
			StringWriter writer = new StringWriter();
			IOUtils.copy(cnx.getInputStream(), writer, content_encoding);
			flv_url = writer.toString();
			String title = flv_url.substring(flv_url.indexOf("&title=")+"&title=".length());
			title = title.indexOf("&sendtmp") > -1 ? title.substring(0, title.indexOf("&sendtmp")) : title;
			flv_url = java.net.URLDecoder.decode(flv_url, content_encoding);
			int sub_string = flv_url.indexOf("url_encoded_fmt_stream_map") + "url_encoded_fmt_stream_map".length() + 1;
			flv_url = flv_url.lastIndexOf("allow_embed") > sub_string ? flv_url.substring(sub_string,flv_url.lastIndexOf("allow_embed") - 1) : flv_url.substring(sub_string);
			flv_url = java.net.URLDecoder.decode(flv_url, content_encoding);
			String[] urls = StringUtils.split(flv_url, ";");
			List<String> nurls = new ArrayList<String>();
			for(int i = 0; i < urls.length; i++){
				String _url = urls[i];
				if(!_url.startsWith("url")){
					String[] parts = StringUtils.split(_url, ",");
					for(String part : parts){
						if(part.startsWith("url")){
							nurls.add(part);
							break;
						}
					}
				}
			}
			String _url = StringUtils.EMPTY;
			for(int i = 0; i < nurls.size(); i++){
				String turl = nurls.get(i);
				if(turl.startsWith("url")){
					_url = turl.substring(4);
					if(turl.indexOf("itag=5") > -1){
						break;
					}
				}
			}
			if(StringUtils.isNotEmpty(_url)){
				URL nurl = new URL(_url);
				String query = nurl.getQuery();
				String[] params = StringUtils.split(query, "&");
				String nparams = StringUtils.EMPTY;
				boolean haveITAG = false;
				for(int i = 0; i < params.length; i++){
					String param = params[i];
					if(!param.startsWith("fallback_host") && !param.startsWith("quality") && !param.startsWith("type") && !param.startsWith("itag")/* && !param.startsWith("signature")*/){
						if(nparams.length() > 0){
							nparams += "&";
						}
						nparams += param;
					}else{
						if(param.startsWith("itag")){
							if(!haveITAG){
								if(nparams.length() > 0){
									nparams += "&";
								}
								nparams += "itag=5";
								haveITAG = true;
							}
						}/*
						if(param.startsWith("signature")){
							if(nparams.length() > 0){
								nparams += "&amp;";
							}
							nparams += "signature=508C15AB0E06711729E00C9582ACF3E360D2ADCF.73630DAD040F2EA922FC0B6F86946A82CCA5DD24";
						}*/
					}
				}
				String link = nurl.toString();
				return link.substring(0, link.indexOf("?")) + "?" + nparams + "&title="+title;
			}else{
				return null;	
			}
		}catch(Exception e){
			log.error(e);
		}finally{
			if(cnx != null){
				cnx.disconnect();
			}
		}
		return null;
	}

	private void sendEmail(String video_id){
		SendMail sendMail = (SendMail)ContextLoaderListener.getBean(SendMail.class);
		sendMail.setTextHtml(true);
		sendMail.setSubject("MUSIC SONAR [ERROR] - Verify MP3 " + video_id);
		//sendMail.setText("Next video have problems to be downloaded :: " + video_id + "<br>Please click next URL :: http://www.youtube-mp3.org/#v=" + video_id);
		sendMail.setText("The following video have problems downloading :: " + video_id + "<br>Please click next URL :: <a href=\""+MP3_DOWNLOAD_SNIPMP3_SERVICE+video_id+"\" target=\"_blank\">Download " + video_id + "</a>");
		sendMail.setFrom(MAIL_USER_FROM);
		sendMail.setTo(MAIL_USER_TO);
		try {
			sendMail.sendMail();
		} catch (Exception e) {
			log.error(e);
		}
	}

	private void sendErrorEmail(Exception error){
		String errorText = exceptionToText(error);
		SendMail sendMail = (SendMail)ContextLoaderListener.getBean(SendMail.class);
		sendMail.setTextHtml(true);
		sendMail.setSubject("MUSIC SONAR [ERROR] - General Error");
		sendMail.setText("The System have the following general error <br>" + errorText);
		sendMail.setFrom(MAIL_USER_FROM);
		sendMail.setTo(MAIL_USER_TO);
		try {
			sendMail.sendMail();
		} catch (Exception e) {
			log.error(e);
		}
	}

	private String exceptionToText(Exception error){
		String result = StringUtils.EMPTY;
		StackTraceElement[] element = error.getStackTrace();
		for(int i = 0; i < element.length; i++){
			result += element[i].toString();
		}
		return result;
	}
}

class NullResolver implements EntityResolver {
	public InputSource resolveEntity(String publicId, String systemId)
			throws SAXException, IOException {
		return new InputSource(new StringReader(StringUtils.EMPTY));
	}
}